<template>
  <div style="width: 100vw;">
    <!--  :title="title"  -->
    <el-dialog v-dialogDrag top="0vh" custom-class="rtc-private-video-dialog" title=""
               fullscreen :show-close="false"
               :visible.sync="showRoom" :close-on-click-modal="false" :close-on-press-escape="false"
               :before-close="onQuit">
      <div class="rtc-private-video">
        <div class="title">
          <div class="custom-btn back flex-center" @click="onQuit">
            <img src="../../assets/image/all/back-camera.png" alt="">
          </div>
          <div class="text">{{ title }}</div>
        </div>
        <div class="rtc-content">
          <!--  video call  -->
          <div v-show="isVideo" class="rtc-video-box">
            <div class="rtc-video-friend" v-loading="!isChating"
                 element-loading-text="Waiting to answer(video)..."
                 element-loading-background="rgba(0, 0, 0, 0.1)">
              <head-image class="friend-head-image" :id="friend.id" :size="80" :name="friend.nickName"
                          :url="friend.headImage" :isShowUserInfo="false" radius="0">
              </head-image>
              <video ref="remoteVideo" autoplay=""></video>
            </div>
            <div class="rtc-video-mine">
              <video ref="localVideo" autoplay=""></video>
            </div>
          </div>
          <!--  voice call  -->
          <div v-show="!isVideo" class="rtc-voice-box" v-loading="!isChating"
               element-loading-text="Waiting to answer(voice)..."
               element-loading-background="rgba(0, 0, 0, 0.1)">
            <head-image class="friend-head-image" :id="friend.id" :size="200" :name="friend.nickName"
                        :url="friend.headImage" :isShowUserInfo="false">
              <div class="rtc-voice-name">{{ friend.nickName }}</div>
            </head-image>
          </div>
        </div>
        <div class="rtc-control-bar">
<!--          <div title="Cancel" class="icon iconfont icon-phone-reject reject" style="color: red;"-->
<!--               @click="onQuit()"></div>-->
          <div class="slot-div"></div>
          <div class="icon-num flex-center hangup-icon" @click="onQuit()">
            <img src="../../assets/image/all/call-hangup.png" alt="hangup">
          </div>
          <div class="slot-div" v-show="!isVideo"></div>
          <div class="icon-num flex-center camera-reverse" @click="flipCamera" v-show="isVideo">
            <img src="../../assets/image/all/camera-reverse-circle.png" alt="hangup">
          </div>
        </div>
      </div>
    </el-dialog>
    <rtc-private-acceptor v-if="!isHost && isWaiting" ref="acceptor" :friend="friend" :mode="mode" @accept="onAccept"
                          @reject="onReject"></rtc-private-acceptor>
  </div>
</template>

<script>
import HeadImage from '../common/HeadImage.vue';
import RtcPrivateAcceptor from './RtcPrivateAcceptor.vue';
import ImWebRtc from '@/api/webrtc';
import ImCamera from '@/api/camera';
import RtcPrivateApi from '@/api/rtcPrivateApi'

export default {
  name: 'rtcPrivateVideo',
  components: {
    HeadImage,
    RtcPrivateAcceptor
  },
  data() {
    return {
      camera: new ImCamera(), // Camera and microphone
      webrtc: new ImWebRtc(), // webrtc
      API: new RtcPrivateApi(), // API
      audio: new Audio(), // Call audio
      showRoom: false, // tmp test => true
      friend: {},
      isHost: false, // Is it the initiator
      state: "CLOSE", // CLOSE  WAITING:Waiting for call or answer CHATING  ERROR
      mode: 'video', // video voice
      localStream: null, // Local video stream
      remoteStream: null,
      videoTime: 0,
      videoTimer: null,
      heartbeatTimer: null,
      candidates: [],
    }
  },
  methods: {
    async flipCamera() {
      if(this.isVideo) {
        try {
          const stream = await this.camera.switchCamera();
          this.localStream = stream;
          this.$nextTick(() => {
            this.$refs.localVideo.srcObject = stream;
            this.$refs.localVideo.muted = true;
          })
        } catch (e) {
          this.$message.error("Failed to switch camera");
        }
      }
    },
    open(rtcInfo) {
      if (rtcInfo) {
        this.showRoom = true;
        this.mode = rtcInfo.mode;
        this.isHost = rtcInfo.isHost;
        this.friend = rtcInfo.friend;
        if (this.isHost) {
          this.onCall();
        }
      }

      // /*
      // * example: rtcInfo
      // * */
      // const exampleRtcInfo = {
      //   "mode": "video",
      //   "isHost": true,
      //   "friend": {
      //     "id": 40,
      //     "userName": "zhangr",
      //     "nickName": "zhangr",
      //     "sex": 1,
      //     "type": 1,
      //     "signature": "",
      //     "headImage": "/getF/2d43e63ca240bdf793814be0dbc9c3a5_b2d9fc849b164b74ba1b4e7710ccfdc3.jpg",
      //     "headImageThumb": "/getF/thumb/2d43e63ca240bdf793814be0dbc9c3a5_b2d9fc849b164b74ba1b4e7710ccfdc3.jpg",
      //     "online": false
      //   }
      // }
      // if (!rtcInfo) {
      //   this.showRoom = true;
      //   this.mode = exampleRtcInfo.mode;
      //   this.isHost = exampleRtcInfo.isHost;
      //   this.friend = exampleRtcInfo.friend;
      //   if (this.isHost) {
      //     this.onCall();
      //   }
      // }
    },
    initAudio() {
      let url = require(`@/assets/audio/call.wav`);
      this.audio.src = url;
      this.audio.loop = true;
    },
    initRtc() {
      console.log('------------initRtc')
      this.webrtc.init(this.configuration)
      this.webrtc.setupPeerConnection((stream) => {
        this.$refs.remoteVideo.srcObject = stream;
        this.remoteStream = stream;
      })
      // Monitor candidate information
      this.webrtc.onIcecandidate((candidate) => {
        if (this.state == "CHATING") {
          // Connection ready, send directly
          this.API.sendCandidate(this.friend.id, candidate);
        } else {
          // Connection not ready, cached, send again after connection
          this.candidates.push(candidate)
        }
      })
      // Monitor the successful connection status
      this.webrtc.onStateChange((state) => {
        if (state == "connected") {
          console.log("WebRTC connection successful")
        } else if (state == "disconnected") {
          console.log("WebRTC connection disconnected")
        }
      })
    },
    onCall() {
      if (!this.checkDevEnable()) {
        this.close();
      }
      this.initRtc();
      this.startHeartBeat();
      // Turn on the camera
      this.openStream().then(() => {
        this.webrtc.setStream(this.localStream);
        this.webrtc.createOffer().then((offer) => {
          // Initiate a call
          this.API.call(this.friend.id, this.mode, offer).then(() => {
            // Directly enter the chat mode
            this.state = "WAITING";
            // Play call ringtone
            this.audio.play();
          }).catch(() => {
            this.close();
          })
        })
      }).catch(() => {
        // The caller must be able to turn on the camera, otherwise the connection cannot be established properly
        this.close();
      })
    },
    onAccept() {
      console.log('onAccept')
      if (!this.checkDevEnable()) {
        console.log('checkDevEnable')
        this.API.failed(this.friend.id, "The other party's device does not support calling")
        this.close();
        return;
      }
      console.log('!checkDevEnable')
      // get into the room
      this.showRoom = true;
      this.state = "CHATING";
      // Stop calling ringtone
      this.audio.pause();
      this.initRtc();
      // Turn on the camera
      this.openStream().finally(() => {
        this.webrtc.setStream(this.localStream);
        this.webrtc.createAnswer(this.offer).then((answer) => {
          this.API.accept(this.friend.id, answer);
          // Record duration
          this.startChatTime();
          // Clean the timer
          this.waitTimer && clearTimeout(this.waitTimer);
        })
      })
    },
    onReject() {
      console.log("onReject")
      // Exit call
      this.API.reject(this.friend.id);
      this.close();
    },
    onHandup() {
      this.API.handup(this.friend.id)
      this.$message.success("You have hung up, the call has ended")
      this.close();
    },
    onCancel() {
      this.API.cancel(this.friend.id)
      this.$message.success("Call cancelled, call ended")
      this.close();
    },
    onRTCMessage(msg) {
      // Apart from initiating a call, there is no need to handle it if it is in the closed state
      if (msg.type != this.$enums.MESSAGE_TYPE.RTC_CALL_VOICE &&
          msg.type != this.$enums.MESSAGE_TYPE.RTC_CALL_VIDEO &&
          this.isClose) {
        return;
      }
      // RTC signaling processing
      switch (msg.type) {
        case this.$enums.MESSAGE_TYPE.RTC_CALL_VOICE:
          this.onRTCCall(msg, 'voice')
          break;
        case this.$enums.MESSAGE_TYPE.RTC_CALL_VIDEO:
          this.onRTCCall(msg, 'video')
          break;
        case this.$enums.MESSAGE_TYPE.RTC_ACCEPT:
          this.onRTCAccept(msg)
          break;
        case this.$enums.MESSAGE_TYPE.RTC_REJECT:
          this.onRTCReject(msg)
          break;
        case this.$enums.MESSAGE_TYPE.RTC_CANCEL:
          this.onRTCCancel(msg)
          break;
        case this.$enums.MESSAGE_TYPE.RTC_FAILED:
          this.onRTCFailed(msg)
          break;
        case this.$enums.MESSAGE_TYPE.RTC_HANDUP:
          this.onRTCHandup(msg)
          break;
        case this.$enums.MESSAGE_TYPE.RTC_CANDIDATE:
          this.onRTCCandidate(msg)
          break;
      }
    },
    onRTCCall(msg, mode) {
      this.offer = JSON.parse(msg.content);
      this.isHost = false;
      this.mode = mode;
      this.$http({
        url: `/friend/find`,
        method: 'POST',
        params: {
          friendId: msg.sendId
        }
      }).then((friend) => {
        this.friend = friend;
        this.state = "WAITING";
        this.audio.play();
        this.startHeartBeat();
        // Automatic hang up if not answered within 30 seconds
        this.waitTimer = setTimeout(() => {
          this.API.failed(this.friend.id, "The other party did not respond");
          this.$message.error("You did not answer");
          this.close();
        }, 30000)
      })
    },
    onRTCAccept(msg) {
      if (msg.selfSend) {
        this.$message.success("Answered on another device");
        this.close();
      } else {
        // The other party accepted the call
        let offer = JSON.parse(msg.content);
        this.webrtc.setRemoteDescription(offer);
        this.state = 'CHATING'
        // Stop playing voice
        this.audio.pause();
        // send candidate
        this.candidates.forEach((candidate) => {
          this.API.sendCandidate(this.friend.id, candidate);
        })
        this.startChatTime()
      }
    },
    onRTCReject(msg) {
      if (msg.selfSend) {
        this.$message.success("Rejected on other devices");
        this.close();
      } else {
        this.$message.error("The other party has refused your call request");
        this.close();
      }
    },
    onRTCFailed(msg) {
      this.$message.error(msg.content)
      this.close();
    },
    onRTCCancel() {
      this.$message.success("The other party cancelled the call");
      this.close();
    },
    onRTCHandup() {
      this.$message.success("The other party has hung up");
      this.close();
    },
    onRTCCandidate(msg) {
      let candidate = JSON.parse(msg.content);
      this.webrtc.addIceCandidate(candidate);
    },

    openStream() {
      return new Promise((resolve, reject) => {
        if (this.isVideo) {
          this.camera.openVideo().then((stream) => {
            this.localStream = stream;
            this.$nextTick(() => {
              this.$refs.localVideo.srcObject = stream;
              this.$refs.localVideo.muted = true;
            })
            resolve(stream);
          }).catch((e) => {
            this.$message.error("Failed to open camera")
            console.log("This camera failed to open:" + e.message)
            reject(e);
          })
        } else {
          this.camera.openAudio().then((stream) => {
            this.localStream = stream;
            this.$refs.localVideo.srcObject = stream;
            resolve(stream);
          }).catch((e) => {
            this.$message.error("Opening microphone failed")
            console.log("Opening microphone failed:" + e.message)
            reject(e);
          })
        }
      })
    },
    startChatTime() {
      this.videoTime = 0;
      this.videoTimer && clearInterval(this.videoTimer);
      this.videoTimer = setInterval(() => {
        this.videoTime++;
      }, 1000)
    },
    checkDevEnable() {
      // Detecting camera
      if (!this.camera.isEnable()) {
        this.message.error("Access to camera failed");
        return false;
      }
      // Detecting WebRTC
      if (!this.webrtc.isEnable()) {
        this.message.error("Initialization of RTC failed, possibly due to: 1. Server missing SSL certificate 2. Your device does not support WebRTC");
        return false;
      }
      return true;
    },
    startHeartBeat() {
      // Push a heartbeat every 15 seconds
      this.heartbeatTimer && clearInterval(this.heartbeatTimer);
      this.heartbeatTimer = setInterval(() => {
        this.API.heartbeat(this.friend.id);
      }, 15000)
    },
    close() {
      this.showRoom = false;
      this.camera.close();
      this.webrtc.close();
      this.audio.pause();
      this.videoTime = 0;
      this.videoTimer && clearInterval(this.videoTimer);
      this.heartbeatTimer && clearInterval(this.heartbeatTimer);
      this.waitTimer && clearTimeout(this.waitTimer);
      this.videoTimer = null;
      this.heartbeatTimer = null;
      this.waitTimer = null;
      this.state = 'CLOSE';
      this.candidates = [];
    },
    onQuit() {
      if (this.isChating) {
        this.onHandup()
      } else if (this.isWaiting) {
        this.onCancel();
      } else {
        this.close();
      }
    }
  },
  computed: {
    width() {
      return this.isVideo ? '960px' : '360px'
    },
    title() {
      let strTitle = `${this.modeText}Call-${this.friend.nickName}`;
      if (this.isChating) {
        strTitle += `(${this.currentTime})`;
      } else if (this.isWaiting) {
        strTitle += `(In Call)`;
      }
      return strTitle;
    },
    currentTime() {
      let min = Math.floor(this.videoTime / 60);
      let sec = this.videoTime % 60;
      let strTime = min < 10 ? "0" : "";
      strTime += min;
      strTime += ":"
      strTime += sec < 10 ? "0" : "";
      strTime += sec;
      return strTime;
    },
    configuration() {
      const iceServers = this.$store.state.configStore.webrtc.iceServers;
      return {
        iceServers: iceServers
      }
    },
    isVideo() {
      return this.mode == "video"
    },
    modeText() {
      return this.isVideo ? "Video" : "Voice";
    },
    isChating() {
      return this.state == "CHATING";
    },
    isWaiting() {
      return this.state == "WAITING";
    },
    isClose() {
      return this.state == "CLOSE";
    }
  },
  mounted() {
    this.initAudio();
  },
  created() {
    window.addEventListener('beforeunload', () => {
      this.onQuit();
    });
  },
  beforeUnmount() {
    this.onQuit();
  }
}
</script>

<style lang="scss" scoped>
::v-deep .el-dialog__body, ::v-deep .el-dialog__header {
  padding: 0 !important;
}

::v-deep .el-dialog {
  background: rgba(13, 18, 23, 1) !important;
}

.rtc-private-video-dialog {
  padding: 0 !important;
}

.rtc-private-video {
  width: 100vw;
  //height: 100vh;
  display: flex;
  flex-direction: column;

  .title {
    display: flex;
    justify-content: space-between;
    align-items: center;
    min-height: 50px;
    padding: 50px 20px 10px;

    .text {
      flex: 1;
      display: flex;
      justify-content: center;
      transform: translate(-20px, 0);
      font-size: 22px;
      color: var(--im-color-white);
    }

    .back, .more {
      background: rgba(255, 255, 255, 0.1);
      box-shadow: 0 4px 12px rgba(13, 10, 44, 0.06);
    }
  }

  .rtc-content {
    //position: absolute;
    //top: 90px;
    //left: 0;
    height: calc(100vh - 260px);
    //flex: 1;
    display: flex;
    margin: 20px 30px 0;
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;
    color: var(--im-color-white);
    //background: rgba(255, 255, 255, .2);
  }

  .el-loading-text {
    color: var(--im-color-white) !important;
    font-size: 16px !important;
  }

  .path {
    stroke: var(--im-color-white) !important;
  }

  .rtc-video-box {
    position: relative;
    width: 100%;
    //height: 100%;
    background-color: #eeeeee;

    .rtc-video-friend {
      height: calc(100vh - 280px);

      .friend-head-image {
        position: absolute;
      }

      video {
        width: 100%;
        height: 100%;
        object-fit: cover;
        transform: rotateY(180deg);
      }
    }

    .rtc-video-mine {
      position: absolute;
      z-index: 99999;
      width: 20vh;
      height: 25vh;
      border-radius: 10px;
      right: 10px;
      bottom: 20px;
      background: #f5f5f5;

      video {
        width: 100%;
        object-fit: cover;
        transform: rotateY(180deg);
      }
    }
  }

  .rtc-voice-box {
    position: relative;
    display: flex;
    justify-content: center;
    align-items: center;
    width: 100%;
    //height: 300px;
    flex: 1;
    background-color: var(--im-color-primary-light-9);

    .rtc-voice-name {
      text-align: center;
      font-size: 20px;
      font-weight: 600;
      color: var(--chat-time-text-color);
    }
  }

  .rtc-control-bar {
    display: flex;
    justify-content: space-around;
    align-items: center;
    padding: 10px;
    min-height: 80px;
    .icon-num,.slot-div {
      width: 50px;
      height: 50px;
    }
    .icon-num {
      border-radius: 50%;
      img {
        width: 100%;
        height: 100%;
      }
    }

    .hangup-icon img {
      width: 60px;
      height: 60px;
    }

    .icon {
      font-size: 50px;
      cursor: pointer;
    }
  }
}

// chat-call
.chat-call-container {

  .call-content {
    flex: 1;
    margin-top: 80px;
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    align-items: center;
    color: var(--im-color-white);

    .avatar {
      width: 120px;
      height: 120px;
      border-radius: 50%;
      background: #fff;
    }

    .name {
      margin-top: 20px;
      font-size: 20px;
    }

    .option-call-wait {
      margin-top: 80px;
      text-align: center;
      display: flex;
      justify-content: space-around;
      gap: 50px;
    }
  }

  .option-calling {
    margin-bottom: 20px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;

    .time-show {
      border-radius: 8px;
      background: rgba(255, 255, 255, 0.5);
      display: flex;
      justify-content: flex-start;
      align-items: center;
      padding: 6px 12px 6px 12px;
      font-size: 16px;
      font-weight: 400;
      letter-spacing: 0;
      line-height: 18.75px;
      color: rgba(13, 18, 23, 1);
      vertical-align: top;
    }

    .option-btn {
      margin-top: 20px;
      display: flex;
      gap: 10px;
    }
  }
}

.call-wait-btn, .calling-btn, .custom-btn {
  background: rgba(255, 255, 255, 0.1);
  box-shadow: 0 4px 12px rgba(13, 10, 44, 0.06);
  width: 45px;
  height: 45px;
  border-radius: 50%;
}

.chat-call-wait {
  background: rgba(13, 18, 23, 1);
}

.chat-calling {
  background: rgba(13, 18, 23, 1);
}
</style>